/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: RangingClient.hpp,v 1.28 2009-09-04 05:29:57 amaula Exp $
 *
 * \file
 * \brief MaCI - Ranging Interface Client module header
 * \author Antti Maula <antti.maula@tkk.fi>
 */

#ifndef _MACI_RANGINGCLIENT_HPP_
#define _MACI_RANGINGCLIENT_HPP_
#include "MaCI.hpp"
#include "thread.hpp"
#include "sync.hpp"
#include "RangingData.hpp"
#include "common/timestamp.hpp"
#include <vector>

// Forward declaration for GIMIMessage
namespace gimi {
  class GIMIMessage;
}

namespace MaCI {
  namespace Ranging {
    

    /** DistanceArray Container
     * This type defines an array of TDistance structures. This structure
     * is used for exporting information to class user.
     */
    typedef std::vector<TDistance> TDistanceArray;
    

    // Forward declare for CRangingClientCallback
    class CRangingClient;
    
    /** Callback interface class for Ranging interface.
     *
     * This interface defines functions which can be used for
     * RangingClient callbacks. These functions are executed (more
     * detail per function basis) as new data arrives, and directly
     * from the context of the receiving thread. <b>Therefore, it is
     * important that the handling function is as simple and fast as
     * possible, as it will block the execution of the receiving
     * thread while executed.</b>
     */
    class CRangingClientCallback
    {
      public:

        CRangingClientCallback() {}
        virtual ~CRangingClientCallback() {}


      /** Handler function for RangingData event.
         *
         * This function is called when the RangingClient receives new
         * data from the server.
         *
         * \note <b>Keep this function simple & fast as it will block the 
         * data receiving thread while executing!</b>
         *
         * @param[in] aData       Constant reference to CPositionData element 
         *                        containing the received event.
         * @param[in] aDataSequence Sequence number of the received data. 
         *                        This should increment by one on each call.
         * @param[in] aRangingClient Reference to RangingClient 
         *                        instance which received this event.
       */
        virtual void OnRangingDataEvent(const CRangingData &aData, 
                                         const unsigned int aDataSequence,
                                         CRangingClient &aRangingClient) = 0;
    };
    
    /** MaCI - RangingClient Interface
     * This class defines a Client module to be used with the 
     * MaCI Ranging interface.
     */
    class CRangingClient : private gim::CSync,
                           private gim::CThread,
                           public CMaCI
                           
    {

    public:
      /** Constructor.
       * 
       * Constructor of the RangingClient. This requires;
       * a pointer to Initialized and connected GIMI, minor
       * number of this interface (Used as GIMI minor number)
       *
       * @param[in] aGIMIPtr Pointer to GIMI used for communication
       * @param[in] aInterfaceMinor Interface minor number. Usually
       * used to indicate the instance of devices of same
       * type.
       */
      CRangingClient(gimi::GIMI *aGIMIPtr, const int aInterfaceMinor = 0);
      ~CRangingClient();

      
      /** Get array of distances.
       *
       * This function is used to read distances from the Ranging Server. 
       * This function can be used in blocking or non-blocking mode. If the
       * sequence number is defined, the function will block until a NEWER
       * scan than the requested sequence is available, and the value is replaced. 
       * (So, successive calls without altering the value will read all available
       * scans in sequence, and blocks between new scans)
       *
       * @param[in] aArray      Array of distances is stored here. The array
       *                        is cleared before filling new information.
       * @param[out] aHeader    Pointer to DistanceHeader. If this is non-null,
       *                        the distanceheader is stored here.
       * @param[out] aTimestamp Pointer to Timestamp. If this is non-null,
       *                        the timestamp of Scan is stored here.
       * @param aSequence       Pointer to sequence number. If this is NULL,
       *                        the most recent scan available is returned immediately.
       *                        If this is not NULL, the value pointed
       *                        should be set to -1 on the first call to this function.
       *                        After each call, the sequence number of the returned
       *                        scan is stored here. If user wants to read all the
       *                        measures available, this value should not be
       *                        modified after the first call.
       * @param[in] aTimeout_ms Timeout value. If the operation is not completed
       *                        before timeout is reached, the operation fails.
       *                        
       * @return                true - Operation was succesfull, new data is stored
       *                        on provided locations.
       *                        false - Operation failed; timeout or generic error
       *                        encountered.
       *
       * \see TDistanceArray
       * \see TDistanceHeader
       * \see TTimestamp
       */
      bool GetDistanceArray(TDistanceArray &aArray,
			    TDistanceHeader *aHeader, 
			    Common::TTimestamp *aTimestamp,
			    int *aSequence, const int aTimeout_ms);
      
      /** Get array of distances as CRangingData container.
       *
       * \overload bool GetDistanceArray(CRangingData &aData, int *aSequence, const int aTimeout_ms);
       *
       * @param[out] aData      Reference to CRangingData container to fill with information.
       * @param aSequence       Pointer to sequence number. If this is NULL,
       *                        the most recent scan available is returned immediately.
       *                        If this is not NULL, the value pointed
       *                        should be set to -1 on the first call to this function.
       *                        After each call, the sequence number of the returned
       *                        scan is stored here. If user wants to read all the
       *                        measures available, this value should not be
       *                        modified after the first call.
       * @param[in] aTimeout_ms Timeout value. If the operation is not completed
       *                        before timeout is reached, the operation fails.* 
       */
      bool GetDistanceArray(CRangingData &aData,
			    int *aSequence, const int aTimeout_ms);

      
      /** Get device limits.
       * 
       * This functions gets the device limits structure from the
       * server. To see what limits are available, see the class TDeviceInformationLimits
       *
       * \see TDeviceInformationLimits
       *
       * @param[out] aLimits    Reference to limits structure which will be
       *                        filled.
       * @param[in] aTimeout_ms Timeout in milliseconds to wait for the operation
       *                        to complete.
       * @param[in] aDirect     If direct is set 'true', the operation will
       *                        always contact the server directly instead
       *                        of using a previously cached value. If set to
       *                        'false', cached value is used if it is available.
       * @return                'true' if the operation was succesfully completed
       *                        within the given timeout. 
       *                        'false' in case of timeout or other error.
       */
      bool GetDeviceLimits(TDeviceInformationLimits &aLimits, const int aTimeout_ms = 1000, 
			   const bool aDirect = false);

      
      /** Get device relative position.
       * 
       * This functions gets the device relative position structure from the
       * server. To see what fields are available, see the class TDeviceInformationPosition
       *
       * \see TDeviceInformationPosition
       *
       * @param[out] aPosition  Reference to position structure which will be
       *                        filled.
       * @param[in] aTimeout_ms Timeout in milliseconds to wait for the operation
       *                        to complete.
       * @param[in] aDirect     If direct is set 'true', the operation will
       *                        always contact the server directly instead
       *                        of using a previously cached value. If set to
       *                        'false', cached value is used if it is available.
       * @return                'true' if the operation was succesfully completed
       *                        within the given timeout. 
       *                        'false' in case of timeout or other error.
       */
      bool GetDevicePosition(TDeviceInformationPosition &aPosition, 
                             const int aTimeout_ms = 1000,
			     const bool aDirect = false);
      
      // Derived from MaCI
      EMaCIError DoOpen(void);
      EMaCIError DoClose(void);

      /** Assigns the Ranging client callback function. 
       *
       * This sets a function object for calling a ranging client
       * callback. If this is set, the interface calls the assigned
       * functions immediately when event is available.
       * 
       * \note As the function is run in the context of the receiving
       * thread, user must make sure that the callback function is
       * fast. As if the function executes too long, receiving events
       * is interrupted.
       *
       * @param[in] aRangingClientCallback Pointer to class implementing the interface
       *                                    CRangingClientCallback.
       */
      void SetRangingClientCallback(CRangingClientCallback *aRangingClientCallback) {
        iRangingClientCallback = aRangingClientCallback;
      }
      
    protected:
    private:
      /**
       * Event types for using with Wait() and Notify() mechanisms.
       */
      enum ERangingEvent { 
        KEventDistanceArray = 0, ///< DistanceArray received
        KEventLimits = 1,        ///< Device limits received
        KEventPosition = 2       ///< Device position received
      };
      
      // variables
    
      /// Generic
      int iMessageSequence;

      /// Device limits
      TDeviceInformationLimits iLimits;
      bool iGotLimits; // Already fetched?

      /// device position
      TDeviceInformationPosition iPosition;
      bool iGotPosition; // Already fetched?

      /// Distance array & header
      std::vector<TDistance> iDistanceArray;
      TDistanceHeader iDistanceHeader;
      Common::TTimestamp iDistanceTimestamp;
      int iLastDistanceArraySequence;

      CRangingClientCallback *iRangingClientCallback;
      
      // Functions
      int ThreadFunction(int threadNumber);
      int ProcessMessage(const gimi::GIMIMessage &msg);

    private:
      CRangingClient(const CRangingClient &) 
        : CSync::CSync(1,1), 
          CThread::CThread(1),
          CMaCI(NULL, 0,0, false),
          iMessageSequence(),
          iLimits(),
          iGotLimits(),
          iPosition(),
          iGotPosition(),
          iDistanceArray(),
          iDistanceHeader(),
          iDistanceTimestamp(),
          iLastDistanceArraySequence(),
          iRangingClientCallback(NULL) {}
      CRangingClient &operator=(const CRangingClient &) { return *this; }
    };
  }
}

#endif

